﻿#include "pch.h"
#include "CubeRenderer.h"

#include "../../DirectXTK/Inc/DDSTextureLoader.h"

using namespace DirectX;
using namespace Microsoft::WRL;
using namespace Windows::Foundation;
using namespace Windows::UI::Core;

CubeRenderer::CubeRenderer() :
	m_loadingComplete(false)
{
}

void CubeRenderer::CreateDeviceResources()
{
	Direct3DBase::CreateDeviceResources();

	// Create DirectXTK objects
	auto device = m_d3dDevice.Get();
	m_states.reset(new CommonStates(device));

	CommonStates states(device);
	NonPremultiplied = states.NonPremultiplied();

	auto context = m_d3dContext.Get();
	m_sprites.reset(new SpriteBatch(context));
	m_batch.reset(new PrimitiveBatch<VertexPositionColorTexture>(context));

	m_batchEffect.reset(new BasicEffect(device));
	m_batchEffect->SetVertexColorEnabled(true);

	{
		void const* shaderByteCode;
		size_t byteCodeLength;

		m_batchEffect->GetVertexShaderBytecode(&shaderByteCode, &byteCodeLength);

		DX::ThrowIfFailed(
			device->CreateInputLayout(VertexPositionColor::InputElements,
				VertexPositionColor::InputElementCount,
				shaderByteCode, byteCodeLength,
				m_batchInputLayout.ReleaseAndGetAddressOf())
			);
	}

	int* pixel = new int[1];
	pixel[0] = -1;
	m_nullTexture = CreateSquareTexture(1, 1, pixel);
	delete pixel;

	m_loadingComplete = true;
}

/// Reference: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476521(v=vs.85).aspx
ID3D11ShaderResourceView* CubeRenderer::CreateSquareTexture(int width, int height, int* texArray)
{
	auto device = m_d3dDevice.Get();

	int size = sizeof(int);
	int length = size * width * height;

	ID3D11Texture2D *boxTex = 0;

	D3D11_TEXTURE2D_DESC boxTexDesc;
	ZeroMemory(&boxTexDesc, sizeof(D3D11_TEXTURE2D_DESC));
	boxTexDesc.ArraySize = 1;
	boxTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	boxTexDesc.CPUAccessFlags = 0;
	boxTexDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	boxTexDesc.Height = height;
	boxTexDesc.MipLevels = 1;
	boxTexDesc.MiscFlags = 0;
	boxTexDesc.SampleDesc.Count = 1;
	boxTexDesc.SampleDesc.Quality = 0; // m_4xMsaaQuality - 1;
	boxTexDesc.Usage = D3D11_USAGE_DEFAULT;
	boxTexDesc.Width = width;

	D3D11_SUBRESOURCE_DATA boxTexInitData;
	ZeroMemory(&boxTexInitData, sizeof(D3D11_SUBRESOURCE_DATA));
	boxTexInitData.pSysMem = texArray;
	boxTexInitData.SysMemPitch = size * width;

	ID3D11ShaderResourceView* texture = nullptr;

	auto h1 = m_d3dDevice->CreateTexture2D(&boxTexDesc, &boxTexInitData, &boxTex);
	auto h2 = m_d3dDevice->CreateShaderResourceView(boxTex, NULL, &texture);

	return texture;
}

void CubeRenderer::CreateWindowSizeDependentResources()
{
	Direct3DBase::CreateWindowSizeDependentResources();

	float aspectRatio = m_windowBounds.Width / m_windowBounds.Height;
	float fovAngleY = 70.0f * XM_PI / 180.0f;
	if (aspectRatio < 1.0f)
	{
		fovAngleY /= aspectRatio;
	}

	// Note that the m_orientationTransform3D matrix is post-multiplied here
	// in order to correctly orient the scene to match the display orientation.
	// This post-multiplication step is required for any draw calls that are
	// made to the swap chain render target. For draw calls to other targets,
	// this transform should not be applied.

	XMMATRIX projection =
		XMMatrixMultiply(
			XMMatrixPerspectiveFovRH(
				fovAngleY,
				aspectRatio,
				0.01f,
				100.0f
				),
			XMLoadFloat4x4(&m_orientationTransform3D));

	m_batchEffect->SetProjection(projection);

	XMStoreFloat4x4(&m_projection, projection);

	DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
	switch (m_orientation)
	{
	case Windows::Graphics::Display::DisplayOrientations::Portrait:
		rotation = DXGI_MODE_ROTATION_IDENTITY;
		break;

	case Windows::Graphics::Display::DisplayOrientations::Landscape:
		rotation = DXGI_MODE_ROTATION_ROTATE90;
		break;

	case Windows::Graphics::Display::DisplayOrientations::PortraitFlipped:
		rotation = DXGI_MODE_ROTATION_ROTATE180;
		break;

	case Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped:
		rotation = DXGI_MODE_ROTATION_ROTATE270;
		break;
	}

	m_sprites->SetRotation(rotation);
}

void CubeRenderer::Update(float timeTotal, float timeDelta)
{
	UNREFERENCED_PARAMETER(timeDelta);

	XMVECTOR eye = XMVectorSet(0.0f, 0.7f, 1.5f, 0.0f);
	XMVECTOR at = XMVectorSet(0.0f, -0.1f, 0.0f, 0.0f);
	XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);

	XMMATRIX view = XMMatrixLookAtRH(eye, at, up);
	XMMATRIX world = XMMatrixRotationY(timeTotal * XM_PIDIV4);

	m_batchEffect->SetView(view);
	m_batchEffect->SetWorld(XMMatrixIdentity());

	XMStoreFloat4x4(&m_view, view);
	XMStoreFloat4x4(&m_world, world);
}

void CubeRenderer::RegisterTexture(Texture^ texture)
{
	if (m_textures.count(texture->ID))
	{
		throw "Texture id already exist.";
	}
	m_textures[texture->ID] = CreateSquareTexture(texture->Width, texture->Height, texture->Data);
}

void CubeRenderer::UnregisterTexture(Texture^ texture)
{
	//ID3D11Resource* res = nullptr;
	//m_textures[texture->ID]->GetResource(&res);
	//res->Release();

	m_textures[texture->ID]->Release();
	m_textures.erase(texture->ID);
}

void CubeRenderer::UpdateTexture(Texture^ texture)
{
	auto d3dtexture = m_textures[texture->ID];
	
	if (d3dtexture == nullptr)
	{
		throw "";
	}
	
	int size = sizeof(int) * texture->Width;
	
	ID3D11Resource* res = nullptr;

	d3dtexture->GetResource(&res);
	
	if (res == nullptr)
	{
		throw "";
	}

	D3D11_BOX box;
	box.left = 0;
	box.right = texture->Width;
	box.top = 0;
	box.bottom = texture->Height;
	box.front = 0;
	box.back = 1;

	m_d3dContext->UpdateSubresource(res, 0, &box, texture->Data, size, 0);
	//m_d3dContext->CopySubresourceRegion(res, 0, nullptr, texture->Data, size, 0);
}

const float midnightBlue[] = { 0.0f, 0.0f, 0.0f, 1.000f };

inline double Distance(double dX0, double dY0, double dX1, double dY1)
{
	return sqrt((dX1 - dX0)*(dX1 - dX0) + (dY1 - dY0)*(dY1 - dY0));
}

void CubeRenderer::BeginRender()
{
	//mBatch.clear();

	// Only draw the cube once it is loaded (loading is asynchronous).
	if (!m_loadingComplete)
	{
		return;
	}

	m_d3dContext->ClearRenderTargetView(
		m_renderTargetView.Get(),
		midnightBlue
		);

	m_d3dContext->ClearDepthStencilView(
		m_depthStencilView.Get(),
		D3D11_CLEAR_DEPTH,
		1.0f,
		0
		);

	m_d3dContext->OMSetRenderTargets(
		1,
		m_renderTargetView.GetAddressOf(),
		m_depthStencilView.Get()
		);

	// Just to trigger something to PrimitiveBatch will renderer anything...
	m_sprites->Begin(DirectX::SpriteSortMode::SpriteSortMode_Immediate, NonPremultiplied);
	RECT rect;
	rect.bottom = 0;
	rect.left = 0;
	rect.right = 0;
	rect.top = 0;
	XMVECTORF32 color;
	m_sprites->Draw(m_nullTexture.Get(), rect, nullptr, color);
	m_sprites->End();

	m_batch->Begin(m_nullTexture.Get());
}

void CubeRenderer::Draw(Vector4 rectangle, Color color)
{
	RECT rect;
	rect.left = rectangle.X;
	rect.top = rectangle.Y;
	rect.right = rectangle.Z;
	rect.bottom = rectangle.W;
	XMFLOAT4 col;
	col.x = color.R;
	col.y = color.G;
	col.z = color.B;
	col.w = color.A;

	DirectX::VertexPositionColorTexture v1(
		XMFLOAT3( (float)rect.left, (float)rect.top, 0 ),
		col,
		XMFLOAT2());
	DirectX::VertexPositionColorTexture v2(
		XMFLOAT3( (float)rect.left, (float)rect.bottom, 0 ),
		col,
		XMFLOAT2());
	DirectX::VertexPositionColorTexture v3(
		XMFLOAT3( (float)rect.right, (float)rect.bottom, 0 ),
		col,
		XMFLOAT2());
	DirectX::VertexPositionColorTexture v4(
		XMFLOAT3( (float)rect.right, (float)rect.top, 0 ),
		col,
		XMFLOAT2());

	// Texture.
	m_batch->DrawQuad(v1, v2, v3, v4);
}

void CubeRenderer::Draw(Vector4 rectangle, Color color, Texture^ texture, Vector4 destinationTextureUV)
{
	RECT rect;
	rect.left = rectangle.X;
	rect.top = rectangle.Y;
	rect.right = rectangle.Z;
	rect.bottom = rectangle.W;
	XMFLOAT4 col;
	col.x = color.R;
	col.y = color.G;
	col.z = color.B;
	col.w = color.A;
	XMFLOAT4 sourceRectangle;

	sourceRectangle.x = (float)(1.0 / texture->Width * destinationTextureUV.X);
	sourceRectangle.y = (float)(1.0 / texture->Height * destinationTextureUV.Y);
	sourceRectangle.z = (float)(1.0 / texture->Width * destinationTextureUV.Z);
	sourceRectangle.w = (float)(1.0 / texture->Height * destinationTextureUV.W);

	DirectX::VertexPositionColorTexture v1(
		XMFLOAT3( (float)rect.left, (float)rect.top, 0 ),
		col,
		XMFLOAT2( sourceRectangle.x, sourceRectangle.y ));
	DirectX::VertexPositionColorTexture v2(
		XMFLOAT3( (float)rect.right, (float)rect.top, 0 ),
		col,
		XMFLOAT2( sourceRectangle.z, sourceRectangle.y ));
	DirectX::VertexPositionColorTexture v3(
		XMFLOAT3( (float)rect.right, (float)rect.bottom, 0 ),
		col,
		XMFLOAT2( sourceRectangle.z, sourceRectangle.w ));
	DirectX::VertexPositionColorTexture v4(
		XMFLOAT3( (float)rect.left, (float)rect.bottom, 0 ),
		col,
		XMFLOAT2( sourceRectangle.x, sourceRectangle.w ));

	m_batch->End();
	m_batch->Begin(m_textures[texture->ID]);
	m_batch->DrawQuad(v1, v2, v3, v4);
	m_batch->End();
	m_batch->Begin(m_nullTexture.Get());
}

void CubeRenderer::DrawLine(Vector2 start, Vector2 end, Color color)
{
	XMFLOAT4 col;
	col.x = color.R;
	col.y = color.G;
	col.z = color.B;
	col.w = color.A;
	// Start.
	Vector2 tl;
	tl.X = start.X;
	tl.Y = start.Y;
	Vector2 bl;
	bl.X = start.X;
	bl.Y = start.Y;

	// Stop.
	Vector2 tr;
	tr.X = end.X;
	tr.Y = end.Y;
	Vector2 br;
	br.X = end.X;
	br.Y = end.Y;

	double xDiff = tr.X - tl.X;
	double yDiff = tr.Y - tl.Y;
	double value = -atan2(yDiff, xDiff);
	double thickness = 1.5;

	tl.X += (float)(sin(value) * thickness);
	tl.Y += (float)(cos(value) * thickness);
	bl.X -= (float)(sin(value) * thickness);
	bl.Y -= (float)(cos(value) * thickness);

	tr.X += (float)(sin(value) * thickness);
	tr.Y += (float)(cos(value) * thickness);
	br.X -= (float)(sin(value) * thickness);
	br.Y -= (float)(cos(value) * thickness);

	DirectX::VertexPositionColorTexture v1(
		XMFLOAT3( (float)tl.X, (float)tl.Y, 0 ),
		col,
		XMFLOAT2());
	DirectX::VertexPositionColorTexture v2(
		XMFLOAT3( (float)bl.X, (float)bl.Y, 0 ),
		col,
		XMFLOAT2());
	DirectX::VertexPositionColorTexture v3(
		XMFLOAT3( (float)br.X, (float)br.Y, 0 ),
		col,
		XMFLOAT2());
	DirectX::VertexPositionColorTexture v4(
		XMFLOAT3( (float)tr.X, (float)tr.Y, 0 ),
		col,
		XMFLOAT2());

	// Texture.
	m_batch->DrawQuad(v1, v2, v3, v4);
}

void CubeRenderer::DrawCircle(Vector2 position, double radius, Color color)
{
	XMFLOAT4 col;
	col.x = color.R;
	col.y = color.G;
	col.z = color.B;
	col.w = color.A;

	int corners = min(8, (int)radius);

	DirectX::VertexPositionColorTexture cCenter(
		XMFLOAT3( (float)position.X, (float)position.Y, 0 ),
		col,
		XMFLOAT2());
	DirectX::VertexPositionColorTexture cFirst;
	DirectX::VertexPositionColorTexture cLast;

	for (size_t i = 0; i < corners; i++)
	{
		// Vertices.
		double angle = (3.14 * 2) / corners * i;
		double x = ::sin(angle) * radius + position.X;
		double y = ::cos(angle) * radius + position.Y;

		DirectX::VertexPositionColorTexture v1(
			XMFLOAT3( (float)x, (float)y, 0 ),
			col,
			XMFLOAT2());

		if (i != 0)
		{
			m_batch->DrawTriangle(cCenter, v1, cLast);
		}
		else
		{
			cFirst = v1;
		}

		cLast = v1;
	}

	m_batch->DrawTriangle(cCenter, cFirst, cLast);
}

void CubeRenderer::DrawTriangles(Vector2* vertices, int verticesLength, unsigned short* indices, int indicesLength, ColorNative* colors, int colorLength)
{
	for (size_t j = 0; j < indicesLength; j += 3)
	{
		auto& vert0(vertices[indices[j + 0]]);
		auto& vert1(vertices[indices[j + 1]]);
		auto& vert2(vertices[indices[j + 2]]);

		auto& col0(colors[indices[j + 0]]);
		auto& col1(colors[indices[j + 1]]);
		auto& col2(colors[indices[j + 2]]);

		DirectX::VertexPositionColorTexture v1(XMFLOAT3( (float)vert0.X, (float)vert0.Y, 0 ), XMFLOAT4( col0.R / 255.0f, col0.G / 255.0f, col0.B / 255.0f, col0.A / 255.0f ), XMFLOAT2());
		DirectX::VertexPositionColorTexture v2(XMFLOAT3( (float)vert1.X, (float)vert1.Y, 0 ), XMFLOAT4( col1.R / 255.0f, col1.G / 255.0f, col1.B / 255.0f, col1.A / 255.0f ), XMFLOAT2());
		DirectX::VertexPositionColorTexture v3(XMFLOAT3( (float)vert2.X, (float)vert2.Y, 0 ), XMFLOAT4( col2.R / 255.0f, col2.G / 255.0f, col2.B / 255.0f, col2.A / 255.0f ), XMFLOAT2());

		// Texture.
		m_batch->DrawTriangle(v1, v2, v3);
	}
}

void CubeRenderer::EndRender()
{
	Render();
}

void CubeRenderer::Render()
{
	m_batch->End();
}

void CubeRenderer::_StopRender()
{
	m_loadingComplete = false;
}

void CubeRenderer::_StartRender()
{
	m_loadingComplete = true;
}
